﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;

namespace Ingenious.Utility
{
    /// <summary>
    /// General helper methods for using runtime reflection for common duties
    /// </summary>
    public class ReflectHelper
    {
        /// <summary>
        /// Gets the name of a property using reflection
        /// </summary>
        /// <typeparam name="TSource">The type that owns the property</typeparam>
        /// <param name="propertyLambda">The expression resolving to the property</param>
        /// <returns></returns>
        public static string GetPropertyName<TSource>(
            Expression<Func<TSource>> propertyLambda)
        {
            MemberExpression memberExpression = propertyLambda.Body as MemberExpression;

            if (memberExpression == null)
            {
                throw new ArgumentNullException("memberExpression");
            }

            return memberExpression.Member.Name;
        }

        /// <summary>
        /// Super charge reflection performance by generating, compiling and caching a
        /// delegate to get the value of a property on the original wrapped entity
        /// </summary>
        /// <param name="method">The reflected method info for the property get accessor</param>
        /// <returns>Compiled expression delegate</returns>
        public static Func<object, object> BuildGetAccessor(MethodInfo method)
        {
            var obj = Expression.Parameter(typeof(object), "o");

            Expression<Func<object, object>> expr =
                Expression.Lambda<Func<object, object>>(
                    Expression.Convert(
                        Expression.Call(
                            Expression.Convert(obj, method.DeclaringType),
                            method),
                        typeof(object)),
                    obj);

            return expr.Compile();
        }

        /// <summary>
        /// Super charge reflection performance by generating, compiling and caching a
        /// delegate to set the value of a property on the original wrapped entity
        /// </summary>
        /// <param name="method">The reflected method info for the property set accessor</param>
        /// <returns>Compiled expression delegate</returns>
        public static Action<object, object> BuildSetAccessor(MethodInfo method)
        {
            var obj = Expression.Parameter(typeof(object), "o");
            var value = Expression.Parameter(typeof(object));

            Expression<Action<object, object>> expr =
                Expression.Lambda<Action<object, object>>(
                    Expression.Call(
                        Expression.Convert(obj, method.DeclaringType),
                        method,
                        Expression.Convert(value, method.GetParameters()[0].ParameterType)),
                    obj,
                    value);

            return expr.Compile();
        }
    }
}
